package io.hellsinger.vortex.service.storage.transaction

import android.os.Parcelable
import androidx.core.app.NotificationCompat
import io.hellsinger.vortex.data.model.OperationState
import io.hellsinger.vortex.data.repository.AndroidStorageOperationStateProducer
import io.hellsinger.vortex.service.notification.TransactionNotifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize

abstract class StorageTransaction {
    var id: Int = NO_ID
        internal set

    private var notifier: TransactionNotifier? = null
    private var dataSource: AndroidStorageOperationStateProducer? = null

    internal fun attachNotificationController(notifier: TransactionNotifier) {
        this.notifier = notifier
    }

    internal fun detachNotificationController() {
        notifier = null
    }

    abstract suspend fun perform(): Result

    fun updateNotification(block: NotificationCompat.Builder.() -> Unit) {
        val notifier = notifier ?: return
        notifier.post(id, notifier.getTransactionChannelBuilder().apply(block).build())
    }

    suspend fun updateOperationState(state: OperationState) {
        val dataSource = dataSource ?: return
        dataSource.update(state)
    }

    suspend inline fun updateOperationState(block: () -> OperationState) = updateOperationState(block())

    fun closeNotification() {
        val notifier = notifier ?: return
        notifier.cancel(id)
    }

    /**
     * Don't add default time parameters (e.g. timeMillis = some time), use explicit, like [DEFAULT_NOTIFICATION_TIMEOUT]
     */
    fun closeNotificationAfter(timeMillis: Long) =
        CoroutineScope(Dispatchers.Main.immediate).launch {
            delay(timeMillis)
            closeNotification()
        }

    sealed interface Result : Parcelable {
        @Parcelize
        data class Error(
            val data: Throwable,
        ) : Result

        @Parcelize
        data object Retry : Result

        @Parcelize
        data object Success : Result
    }

    companion object {
        const val NO_ID = -1

        const val DEFAULT_NOTIFICATION_TIMEOUT = 1000L // 1 sec
    }
}
